﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Collections.Specialized;
using System.Xml;

using Consejo.Monitoring.BusinessObjects;

using Devv.Core.Crypto;
using Devv.Core.Utils;

namespace Consejo.Monitoring.Destinations
{
    public class Panopta : IMonitorDestination
    {
        #region Constants
        private const string PANOPTA_URI = "agent.panopta.com/StatusReport/";
        private const string PANOPTA_ENCRYPTION_KEY = "[INSERT YOUR ENCRYPTION KEY]";
        #endregion

        #region Sending Status Methods

        bool IMonitorDestination.SendStatus(SystemStatus _systemStatus)
        {
            bool _returnValue = false;

            NameValueCollection _statusData = new NameValueCollection();
            _statusData.Add("xml", GetEncryptedString(SerializeStatusData(_systemStatus).OuterXml));

            string _postUrl = PANOPTA_URI + _systemStatus.SystemId;

            try
            {
                Encoding.ASCII.GetString(PostWebData(_postUrl, GetHeaders(EncryptionType.des3), _statusData));
                _returnValue = true; 
            }
            catch (WebException ex)
            {
                _returnValue = false;
            }

            return _returnValue;
        }

        public string SendStatus(SystemStatus _systemStatus, EncryptionType _encryptionType)
        {
            string _returnValue = string.Empty;

            NameValueCollection _statusData = new NameValueCollection();
            _statusData.Add("xml", GetEncryptedString(SerializeStatusData(_systemStatus).OuterXml));

            string _postUrl = PANOPTA_URI + _systemStatus.SystemId;

            try
            {
                _returnValue = Encoding.ASCII.GetString(PostWebData(_postUrl, GetHeaders(_encryptionType), _statusData));
            }
            catch (WebException ex)
            {
                _returnValue = ex.Status.ToString() + " :: " + ex.Message;
            }

            return _returnValue;
        }

        public string SendStatus(SystemStatus _systemStatus, EncryptionType _encryptionType, string _monitorUrl)
        {
            string _returnValue = string.Empty;

            NameValueCollection _statusData = new NameValueCollection();
            _statusData.Add("xml", GetEncryptedString(SerializeStatusData(_systemStatus).OuterXml));

            string _postUrl = _monitorUrl;

            try
            {
                _returnValue = Encoding.ASCII.GetString(PostWebData(_postUrl, GetHeaders(_encryptionType), _statusData));


            }
            catch (WebException ex)
            {
                _returnValue = ex.Status.ToString() + " :: " + ex.Message;
            }

            return _returnValue;

        }

        public string SendStatus(SystemStatus _systemStatus, string userName, string password)
        {
            string _returnValue = string.Empty;

            NameValueCollection _statusData = new NameValueCollection();
            _statusData.Add("xml", SerializeStatusData(_systemStatus).OuterXml);

            string _postUrl = PANOPTA_URI + _systemStatus.SystemId;

            try
            {
                _returnValue = Encoding.ASCII.GetString(PostWebData(_postUrl, GetHeaders(userName, password), _statusData));
            }
            catch (WebException ex)
            {
                _returnValue = ex.Status.ToString() + " :: " + ex.Message;
            }

            return _returnValue;
        }

        public string SendStatus(SystemStatus _systemStatus, string userName, string password, string _monitorUrl)
        {
            string _returnValue = string.Empty;

            NameValueCollection _statusData = new NameValueCollection();
            _statusData.Add("xml", SerializeStatusData(_systemStatus).OuterXml);

            string _postUrl = _monitorUrl;

            try
            {
                _returnValue = Encoding.ASCII.GetString(PostWebData(_postUrl, GetHeaders(userName, password), _statusData));
            }
            catch (WebException ex)
            {
                _returnValue = ex.Status.ToString() + " :: " + ex.Message;
            }

            return _returnValue;
        }

        #endregion

        #region Web Client Communication Methods

        private byte[] PostWebData(string _postUrl, WebHeaderCollection _headers, NameValueCollection _statusData)
        {
            byte[] _returnValue = null;
            using (System.Net.WebClient _webClient = new System.Net.WebClient())
            {
                _webClient.Headers = _headers;
                _returnValue = _webClient.UploadValues(_postUrl, _statusData);
            }
            return _returnValue;
        }

        private System.Net.WebHeaderCollection GetHeaders(EncryptionType _encryptionType)
        {
            System.Net.WebHeaderCollection _headers = new System.Net.WebHeaderCollection();
            _headers.Add("Content-Type", "application/x-www-form-urlencoded");
            _headers.Add("X-Panopta-Payload-Format", "xml");
            _headers.Add("X-Panopta-Encryption", _encryptionType.ToString());

            return _headers;
        }

        private System.Net.WebHeaderCollection GetHeaders(string userName, string password)
        {
            System.Net.WebHeaderCollection _headers = new System.Net.WebHeaderCollection();
            _headers.Add("Content-Type", "application/x-www-form-urlencoded");
            _headers.Add("X-Panopta-Payload-Format", "xml");
            _headers.Add("X-Panopta-Encryption", EncryptionType.none.ToString());
            _headers.Add("X-Panopta-Username", userName);
            _headers.Add("X-Panopta-Password", password);

            return _headers;
        }

        #endregion

        #region Serialization Methods

        private XmlDocument SerializeStatusData(SystemStatus _systemStatus)
        {
            XmlDocument _objectDocument = new XmlDocument();
            XmlElement _serverStatus;
            XmlElement _rootStatusNode = _objectDocument.CreateElement("StatusUpdate");
            foreach (ServerStatus _server in _systemStatus.Servers)
            {
                _serverStatus = XmlUtility.CreateXmlNode(_objectDocument, "Server");
                _serverStatus.Attributes.Append(XmlUtility.CreateAttribute(_objectDocument, "id", _server.ServerId.ToString()));
                foreach (IStatusItem _statusItem in _server.StatusItems)
                {
                    _serverStatus.AppendChild(CreateStatusItemNode(_objectDocument, _statusItem));
                }
                _rootStatusNode.AppendChild(_serverStatus);
            }

            _objectDocument.AppendChild(_rootStatusNode);

            return _objectDocument;
        }


        private XmlElement CreateStatusItemNode(XmlDocument _xmlDocument, IStatusItem _statusItem)
        {
            XmlElement _node;

            // Depending on the status item type, the node is either Resource or Service
            if (_statusItem.GetType().ToString() == "Consejo.Monitoring.BusinessObjects.ResourceStatus")
                _node = XmlUtility.CreateXmlNode(_xmlDocument, "Resource");
            else
                _node = XmlUtility.CreateXmlNode(_xmlDocument, "Service");

            // Add the ID to the status item.  Panopta referers to all status elements by ID
            _node.Attributes.Append(XmlUtility.CreateAttribute(_xmlDocument, "id", _statusItem.ID.ToString()));

            // Panopta requires time in UTC and formatted as follows: YYYY-MM-DD HH:mm:ss where HH represents 24 hour time
            _node.Attributes.Append(XmlUtility.CreateAttribute(_xmlDocument, "timestamp", _statusItem.TimeStamp.ToString("yyyy-MM-dd HH:mm:ss")));

            // Depending on the status type, the last attribute is either value or result
            if (_statusItem.GetType().ToString().Contains("Resource"))
                _node.Attributes.Append(XmlUtility.CreateAttribute(_xmlDocument, "value", ((double)_statusItem.Result).ToString()));
            else
            {
                _node.Attributes.Append(XmlUtility.CreateAttribute(_xmlDocument, "result", ((bool)_statusItem.Result).ToString()));
                _node.Attributes.Append(XmlUtility.CreateAttribute(_xmlDocument, "duration", ((ServiceStatus)_statusItem).Duration.ToString()));
            }

            return _node;
        }

        #endregion

        #region Encryption Routines
        private string GetEncryptedString(string _unencryptedString, EncryptionType _encryptionType)
        {
            string _encryptedString = string.Empty;

            if (_encryptionType = EncryptionType.des3)
                _encryptedString = Devv.Core.Crypto.Cipher.TripleDESEncrypt(_unencryptedString, PANOPTA_ENCRYPTION_KEY);
            else
                _encryptedString = Devv.Core.Crypto.Cipher.AESEncrypt(_unencryptedString, PANOPTA_ENCRYPTION_KEY);

            return _encryptedString;
        }

        private string GetEncryptedString(string _unencryptedString)
        {
            return GetEncryptedString(_unencryptedString, EncryptionType.des3);
        }
        #endregion

    }
}

public enum EncryptionType
{ none, aes, des3 }
